#include "parser.h"
#include <cassert>

// prog : (decl-stmt | expr-stmt)*
// decl-stmt : "int" identifier ("," identifier (= expr)?)* ";"
// expr-stmt : expr? ";"
// expr : assign-expr | add-expr
// assign-expr: identifier "=" expr
// add-expr : mult-expr (("+" | "-") mult-expr)*
// mult-expr : primary-expr (("*" | "/") primary-expr)*
// primary-expr : identifier | number | "(" expr ")"
// number: ([0-9])+
// identifier : (a-zA-Z_)(a-zA-Z0-9_)*

// 解析目标程序
std::shared_ptr<Program> Parser::ParseProgram()
{
    std::vector<std::shared_ptr<ASTNode>> ExprVec;
    while (token.tokenType != TokenType::eof)
    {
        // 遇到 ; 需要进行消费 token
        if (token.tokenType == TokenType::semi)
        {
            Advance();
            continue;
        }
        if (token.tokenType == TokenType::kw_int)
        {
            auto exprs = ParseDecl();
            for (auto expr : exprs)
            {
                ExprVec.push_back(expr);
            }
        }
        else
        {
            auto expr = ParseExpr();
            ExprVec.push_back(expr);
        }
    }
    auto program = std::make_shared<Program>();
    program->ExprVec = std::move(ExprVec);
    return program;
}

// 解析声明
std::vector<std::shared_ptr<ASTNode>> Parser::ParseDecl()
{
    /// int a, b = 3;
    Consume(TokenType::kw_int);
    CType *baseTy = CType::getIntTy();

    std::vector<std::shared_ptr<ASTNode>> astArr;

    /// a, b = 3;
    int i = 0;
    while (token.tokenType != TokenType::semi)
    {
        if (i++ > 0) // if (i++)
        {
            assert(Consume(TokenType::comma));
        }

        /// 变量声明的节点: int a = 3; -> int a; a = 3;
        // a = 3;
        auto varName = token.content;
        auto variableDecl = sema.SemaVariableDeclNode(varName, baseTy); // get a type
        astArr.push_back(variableDecl);
        Consume(TokenType::indentifier);

        // = 3;
        if (token.tokenType == TokenType::equal)
        {
            llvm::StringRef name = varName;
            Advance();

            // 3;
            auto right = ParseExpr();
            auto left = sema.SemaVariableAccessNode(name);
            auto assign = sema.SemaAssignExprNode(left, right);

            astArr.push_back(assign);
        }
    }

    Advance();

    return astArr;
}

// 解析表达式
std::shared_ptr<ASTNode> Parser::ParseExpr()
{
    std::shared_ptr<ASTNode> left = ParseTerm();

    while (token.tokenType == TokenType::plus || token.tokenType == TokenType::minus)
    {
        OpCode op;
        if (token.tokenType == TokenType::plus)
        {
            op = OpCode::add;
        }
        else
        {
            op = OpCode::sub;
        }
        Advance();
        auto right = ParseTerm();
        auto binaryExpr = sema.SemaBinaryExprNode(left, right, op);

        left = binaryExpr;
    }
    return left;
}

// 解析项
std::shared_ptr<ASTNode> Parser::ParseTerm()
{
    std::shared_ptr<ASTNode> left = ParseFactor();

    while (token.tokenType == TokenType::star || token.tokenType == TokenType::slash)
    {
        OpCode op;
        if (token.tokenType == TokenType::star)
        {
            op = OpCode::mul;
        }
        else
        {
            op = OpCode::div;
        }
        Advance();
        auto right = ParseFactor();
        auto binaryExpr = sema.SemaBinaryExprNode(left, right, op);

        left = binaryExpr;
    }
    return left;
}

// 解析因子
std::shared_ptr<ASTNode> Parser::ParseFactor()
{
    if (token.tokenType == TokenType::l_parent)
    {
        Advance();
        auto expr = ParseExpr();
        assert(Expect(TokenType::r_parent));
        Advance();
        return expr;
    }
    else if(token.tokenType == TokenType::indentifier)
    {
        auto variableAccessExpr = sema.SemaVariableAccessNode(token.content);
        Advance();
        return variableAccessExpr;
    }
    else
    {
        auto factorExpr = sema.SemaNumberExprNode(token.value, token.type);
        Advance();
        return factorExpr;
    }
}

/// 消耗 token 函数
bool Parser::Expect(TokenType tokenType)
{
    return token.tokenType == tokenType;
}

bool Parser::Consume(TokenType tokenType)
{
    if (Expect(tokenType))
    {
        Advance();
        return true;
    }
    return false;
}

bool Parser::Advance()
{
    lexer.NextToken(token);
    return true;
}